package com.hzw.saas.service.product.service;

import java.util.Date;
import java.util.Objects;
import javax.annotation.Resource;

import com.hzw.saas.api.hpc.IHpcAccountUserService;
import com.hzw.saas.api.hpc.bo.HpcAccountRecordBo;
import com.hzw.saas.api.product.*;
import com.hzw.saas.api.product.dto.*;
import com.hzw.saas.api.product.enums.OrderStatus;
import com.hzw.saas.api.product.enums.OrderType;
import com.hzw.saas.api.product.enums.PayMode;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.pay.PayExecutor;
import com.hzw.saas.common.pay.bean.PayContent;
import com.hzw.saas.common.pay.bean.PayUrl;
import com.hzw.saas.common.pay.bean.QueryContent;
import com.hzw.saas.common.pay.bean.TradeStatus;
import com.hzw.saas.common.pay.enums.PayType;
import com.hzw.saas.common.pay.exception.TradeException;
import com.hzw.saas.common.util.UnitConvertUtils;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;

/**
 * 支付业务接口默认实现
 *
 * @author zzl
 * @since 01/28/2021
 */
@Service("payServiceImpl")
@RequiredArgsConstructor
@Slf4j
public class PayServiceImpl implements IPayService {

    private final MapperFacade mapperFacade;
    @Resource(name = "orderCacheServiceImpl")
    private IOrderCacheService orderCacheService;
    @Resource(name = "itemOrderServiceImpl")
    private IItemOrderService itemOrderService;
    @Resource(name = "balanceOrderServiceImpl")
    private IBalanceOrderService balanceOrderService;
    @Resource(name = "paymentOnlineServiceImpl")
    private IPaymentOnlineService paymentOnlineService;
    @Resource(name = "hpcAccountUserServiceImpl")
    private IHpcAccountUserService hpcAccountUserService;
    @Resource(name = "userBalanceServiceImpl")
    private IUserBalanceService userBalanceService;

    @Override
    public PayUrlDto qrCodeGenerate(OrderCacheDto orderCacheDto, int qrCodeWith) {
        PayContent payContent = initPayCache(orderCacheDto);
        PaymentOnlineDto payment = orderCacheDto.getPayment();
        PayType payType = PayType.instance(payment.getPayType());
        if (payType == null) {
            throw DefaultSaasException.build("支付类型异常：" + payment.getPayType()).status(HttpStatus.BAD_REQUEST);
        }
        try {
            if (qrCodeWith <= 0) {
                qrCodeWith = -1;
            }
            PayUrl payUrl = PayExecutor.qrcodePay(payContent, payType, qrCodeWith);
            PayUrlDto payUrlDto = mapperFacade.map(payUrl, PayUrlDto.class);
            payUrlDto.setPayNo(payment.getPayNo());
            return payUrlDto;
        } catch (TradeException e) {
            orderCacheService.deleteByOrder(orderCacheDto.getPayment().getOrderId());
            String errorMsg = String.format("获取[%s]支付链接异常", payType.text());
            log.error(errorMsg, e);
            throw DefaultSaasException.build(errorMsg);
        }
    }

    @Override
    public PayUrlDto pagePayGenerate(OrderCacheDto orderCacheDto) {
        PayContent payContent = initPayCache(orderCacheDto);
        PaymentOnlineDto payment = orderCacheDto.getPayment();
        PayType payType = PayType.instance(payment.getPayType());
        if (payType == null) {
            throw DefaultSaasException.build("支付类型异常：" + payment.getPayType()).status(HttpStatus.BAD_REQUEST);
        }
        try {
            PayUrl payUrl = PayExecutor.pagePay(payContent, payType);
            PayUrlDto payUrlDto = mapperFacade.map(payUrl, PayUrlDto.class);
            payUrlDto.setPayNo(payment.getPayNo());
            return payUrlDto;
        } catch (TradeException e) {
            orderCacheService.deleteByOrder(orderCacheDto.getPayment().getOrderId());
            String errorMsg = String.format("获取[%s]支付链接异常", payType.text());
            log.error(errorMsg, e);
            throw DefaultSaasException.build(errorMsg);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderStatusDto pay(String payNo, String payNoBiz, Integer payType, String userId) {
        // 检查是否已有支付持久化信息
        PaymentOnlineDto paymentExists = paymentOnlineService.getByPayNo(payNo);
        if (paymentExists != null) {
            return getOrderStatusByPayment(paymentExists);
        }
        Date now = new Date();
        OrderCacheDto orderCacheDto = orderCacheService.getByPayNo(payNo);
        if (orderCacheDto == null) {
            log.error("订单支付成功，无法获取订单缓存，payNo=" + payNo);
            throw DefaultSaasException.build("内部订单错误，请联系管理员退款");
        }
        // 支付信息
        PaymentOnlineDto payCacheDto = orderCacheDto.getPayment();
        if (payCacheDto == null) {
            orderCacheService.deleteByPayNo(payNo);
            log.error("订单支付成功，无法获取订单支付缓存，payNo=" + payNo);
            throw DefaultSaasException.build("内部订单错误，请联系管理员退款");
        }

        // 检查权限
        AssertUtil.assertThrow("请求非法！", HttpStatus.FORBIDDEN,
            StrUtil.isNotBlank(userId) && !ObjectUtil.notEqual(userId, payCacheDto.getUserId()));

        // 设置支付信息
        payCacheDto.setPayNoBiz(payNoBiz)
            .setPayType(payType)
            .setPayStatus(OrderStatus.PAY_SUCCESS.value())
            .setPayTime(now);
        // 持久化支付信息
        paymentOnlineService.add(payCacheDto);

        // 使用余额支付
        if (Objects.equals(payCacheDto.getPayType(), PayType.BALANCE_PAY.value())) {
            userBalanceService.subBalance(payCacheDto.getUserId(), payCacheDto.getPayAmount());
        }

        // 设置支付结果
        OrderStatusDto orderStatusDto = new OrderStatusDto()
            .setOrderId(payCacheDto.getOrderId())
            .setPayNo(payCacheDto.getPayNo())
            .setPayType(payCacheDto.getPayType())
            .setStatus(OrderStatus.PAY_SUCCESS.value());

        // 订单信息
        OrderType orderType = OrderType.instance(payCacheDto.getOrderType());
        if (orderType == null) {
            log.error("订单支付成功，无法获取订单类型，payNo=" + payNo);
            throw DefaultSaasException.build("内部订单错误，请联系管理员退款");
        }
        // 持久化订单信息
        switch (orderType) {
            case ITEM:
                // 购买订单
                ItemOrderDto itemOrderDto = orderCacheDto.getItemOrder();
                if (itemOrderDto == null) {
                    log.error("订单支付成功，无法获取购买订单，payNo=" + payNo);
                    throw DefaultSaasException.build("内部订单错误，请联系管理员退款");
                }
                if (itemOrderDto.getCreateTime() == null) {
                    itemOrderDto.setCreateTime(now);
                }
                itemOrderDto.setPayTime(now)
                    .setFinallyTime(now)
                    .setIsPayed(1)
                    .setPayMode(PayMode.ONLINE_PAY.value())
                    .setStatus(OrderStatus.PAY_SUCCESS.value());

                String hpcAccountId = itemOrderDto.getPid();
                if (StringUtil.isNotBlank(hpcAccountId)) {
                    // 标识当前帐号为正在续费状态，避免因过期被分配至其他用户
//                    try {
//                        hpcAccountUserService.tagHpcAccount(hpcAccountId);
//                    } catch (Exception e) {
//                        throw DefaultSaasException.build("内部订单错误，请联系管理员退款，payNo=" + payNo, e);
//                    }
                }
                HpcAccountRecordBo hpcAccountUserBo = null;
//                    hpcAccountUserService.get(hpcAccountId);
                // 持久化购买订单数据
                itemOrderDto.setPid(hpcAccountUserBo.getPid());
                itemOrderService.add(itemOrderDto);
                // 返回购买订单信息
                Date startDate = new Date();
//                Date startDate = hpcAccountUserBo == null || hpcAccountUserBo.getExpireDate() == null ? new Date() : hpcAccountUserBo.getExpireDate();
                orderStatusDto.setOrderType(OrderType.ITEM.value());
                orderStatusDto.setItemOrder(
                    new ItemOrderSuccessDto(itemOrderDto.getItemName(),
                        UnitConvertUtils.offsetDatetime(startDate, itemOrderDto.getItemDatePeriod(), itemOrderDto.getItemDateUnit())));
                break;
            case BALANCE:
                // 充值订单
                BalanceOrderDto balanceOrderDto = orderCacheDto.getBalanceOrder();
                if (balanceOrderDto == null) {
                    log.error("订单支付成功，无法获取充值订单，payNo=" + payNo);
                    throw DefaultSaasException.build("内部订单错误，请联系管理员退款");
                }
                if (balanceOrderDto.getCreateTime() == null) {
                    balanceOrderDto.setCreateTime(now);
                }
                balanceOrderDto.setPayTime(now)
                    .setFinallyTime(now)
                    .setIsPayed(1)
                    .setPayMode(PayMode.ONLINE_PAY.value())
                    .setStatus(OrderStatus.PAY_SUCCESS.value());
                balanceOrderService.add(balanceOrderDto);

                UserBalanceDto userBalanceDto = userBalanceService.get(balanceOrderDto.getUserId());
//                double balance = userBalanceService.addBalance(balanceOrderDto.getUserId(), balanceOrderDto.getPayValue());
                // 返回充值订单信息
                orderStatusDto.setOrderType(OrderType.BALANCE.value());
                orderStatusDto.setBalanceOrder(new BalanceOrderSuccessDto(NumberUtil.add(userBalanceDto.getBalance(), balanceOrderDto.getPayValue())));
                break;
        }

        // 删除缓存
        orderCacheService.deleteByOrder(payCacheDto.getOrderId());
        return orderStatusDto;
    }

    @Override
    public OrderStatusDto queryPayStatus(String payNo, String userId) {
        if (StrUtil.isBlank(payNo)) {
            throw DefaultSaasException.build("payNo 为空").status(HttpStatus.BAD_REQUEST);
        }
        // 检查是否已有支付持久化信息
        PaymentOnlineDto paymentExists = paymentOnlineService.getByPayNo(payNo);
        if (paymentExists != null) {
            // 检查权限
            AssertUtil.assertThrow("请求非法！", HttpStatus.FORBIDDEN,
                StrUtil.isNotBlank(userId) && !ObjectUtil.notEqual(userId, paymentExists.getUserId()));
            return getOrderStatusByPayment(paymentExists);
        }
        // 无订单持久化信息，获取订单缓存
        OrderCacheDto orderCacheExists = orderCacheService.getByPayNo(payNo);
        if (orderCacheExists == null) {
            throw DefaultSaasException.build("无此订单信息").status(HttpStatus.BAD_REQUEST);
        }
        PaymentOnlineDto paymentCacheExists = orderCacheExists.getPayment();
        // 检查权限
        AssertUtil.assertThrow("请求非法！", HttpStatus.FORBIDDEN,
            StrUtil.isNotBlank(userId) && !ObjectUtil.notEqual(userId, paymentCacheExists.getUserId()));
        // 支付结果信息
        OrderStatusDto orderStatusDto = new OrderStatusDto()
            .setOrderId(paymentCacheExists.getOrderId())
            .setPayNo(paymentCacheExists.getPayNo())
            .setPayType(paymentCacheExists.getPayType())
            .setStatus(OrderStatus.UNKNOWN.value());
        // 访问第三方支付,查询支付状态
        QueryContent queryContent = new QueryContent();
        queryContent.setPayNo(payNo);
        queryContent.setCreateTime(paymentCacheExists.getCreateTime());
        try {
            TradeStatus query = PayExecutor.query(queryContent, PayType.instance(paymentCacheExists.getPayType()));
            switch (query.getTradeStatus()) {
                case TRADE_SUCCESS:
                case TRADE_FINISHED:
                    return this.pay(query.getPayNo(), query.getBizPayNo(), query.getPayType().value(), userId);
                case WAIT_BUYER_PAY:
                    return orderStatusDto.setStatus(OrderStatus.UNKNOWN.value());
                case TRADE_CLOSED:
                    return orderStatusDto.setStatus(OrderStatus.CLOSE.value());
            }
        } catch (TradeException e) {
            // TODO: ZZL@02/07/2021 异常记录暂时关闭
            // log.error("查询支付状态失败", e);
        }
        return orderStatusDto;
    }

    private PayContent initPayCache(OrderCacheDto orderCacheDto) {
        if (orderCacheDto == null) {
            throw DefaultSaasException.build("订单缓存失效，请重试");
        }
        // 支付信息
        PaymentOnlineDto payment = orderCacheDto.getPayment();
        if (payment == null) {
            throw DefaultSaasException.build("内部缓存异常，无法获取支付信息缓存");
        }
        // 订单信息
        String title = "";
        OrderType orderType = OrderType.instance(payment.getOrderType());
        if (orderType == null) {
            throw DefaultSaasException.build("订单类型异常");
        }
        switch (orderType) {
            // 购买订单
            case ITEM:
                ItemOrderDto itemOrder = orderCacheDto.getItemOrder();
                if (itemOrder == null) {
                    throw DefaultSaasException.build("内部缓存异常，无法获取购买订单缓存");
                }
                title = itemOrder.getItemName();
                break;
            // 充值订单
            case BALANCE:
                BalanceOrderDto balanceOrder = orderCacheDto.getBalanceOrder();
                if (balanceOrder == null) {
                    throw DefaultSaasException.build("内部缓存异常，无法获取充值订单缓存");
                }
                title = "Saas账户充值";
                break;
        }

        // 生成支付二维码或支付跳转链接
        Date now = new Date();
        PayContent payContent = new PayContent();
        payContent.setPayNo(payment.getPayNo())
            .setTotalAmount(payment.getPayAmount())
            .setCreateTime(payment.getCreateTime())
            .setPayTitle(title)
            .setExpireDate(DateUtil.offsetHour(now, 1));
        return payContent;
    }

    private OrderStatusDto getOrderStatusByPayment(PaymentOnlineDto payment) {
        if (payment == null) {
            return new OrderStatusDto().setStatus(OrderStatus.UNKNOWN.value());
        }
        OrderStatusDto orderStatusDto = new OrderStatusDto()
            .setOrderId(payment.getOrderId())
            .setPayNo(payment.getPayNo())
            .setPayType(payment.getPayType())
            .setStatus(payment.getPayStatus());

        // 获取购买/充值订单信息
        if (Objects.equals(payment.getOrderType(), OrderType.ITEM.value())) {
            ItemOrderDto itemOrderDto = itemOrderService.get(payment.getOrderId());
            if (itemOrderDto == null) {
                String errorMsg = String.format("获取订单异常,orderId: %s", payment.getOrderId());
                log.error(errorMsg);
                throw DefaultSaasException.build(errorMsg);
            }
            HpcAccountRecordBo hpcAccountUserBo = null;
//                hpcAccountUserService.get(itemOrderDto.getPid());
            if (hpcAccountUserBo == null) {
                String errorMsg = String.format("获取超算账户异常,userId: %s", payment.getUserId());
                log.error(errorMsg);
                throw DefaultSaasException.build(errorMsg);
            }
            orderStatusDto.setOrderType(OrderType.ITEM.value());
//            hpcAccountUserBo.getExpireDate()
            orderStatusDto.setItemOrder(new ItemOrderSuccessDto(itemOrderDto.getItemName(), new Date()));
        } else if (Objects.equals(payment.getOrderType(), OrderType.BALANCE.value())) {
            UserBalanceDto userBalanceDto = userBalanceService.get(payment.getUserId());
            if (userBalanceDto == null) {
                String errorMsg = "用户余额账户获取异常，userId: " + payment.getUserId();
                log.error(errorMsg);
                throw DefaultSaasException.build(errorMsg);
            }
            orderStatusDto.setOrderType(OrderType.BALANCE.value());
            orderStatusDto.setBalanceOrder(new BalanceOrderSuccessDto(userBalanceDto.getBalance()));
        }
        return orderStatusDto;
    }

}
